package org.beholder;


import javax.imageio.ImageIO;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.sql.DataSource;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.*;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

/**
 * Created by IntelliJ IDEA.
 * User: ddreval
 * Date: 02.10.12
 * Time: 18:39
 * To change this template use File | Settings | File Templates.
 */
public class AbstractServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

    private static final int PREVIEW_WIDTH = 90;
    private static final int PREVIEW_HEIGHT = 60;

    public static final String STATE_PARAM = "state";
    public static final String STATE_OFF = "OFF";
    public static final String STATE_PRW = "PRW";
    public static final String STATE_REC = "REC";
    public static final String STATE_HD = "HD";

	private Logger logger = Logger.getLogger(AbstractServlet.class);

    protected Connection conn;
    protected PreparedStatement ps = null;
    protected ResultSet rs = null;

    protected PreparedStatement PS_STORE_SHOT;
    protected PreparedStatement PS_UPDATE_PREVIEW;

    @Override
    public void init() throws ServletException {

    	super.init();

    	try {
    		System.out.println("Setting Logger properties");
        	Properties properties = new Properties();
        	properties.setProperty("log4j.appender.file", "org.apache.log4j.FileAppender");
        	properties.setProperty("log4j.appender.file.layout", "org.apache.log4j.PatternLayout");
        	properties.setProperty("log4j.appender.file.layout.ConversionPattern", "%d{MMM dd HH:mm:ss,SSS} %-5p [%t] %c{2} - %m%n");
        	properties.setProperty("log4j.appender.file.File", "beholder.log");
        	properties.setProperty("log4j.appender.file.Append", "false");
        	properties.setProperty("log4j.rootLogger", "INFO, file");
        	properties.setProperty("log4j.logger.org.beholder", "TRACE");
            PropertyConfigurator.configure(properties);
            System.out.println("Logger properties set");
        } catch (Exception e) {
        	e.printStackTrace();
        }

        try {
            InitialContext initialContext = new InitialContext();
            DataSource dataSource = (DataSource)initialContext.lookup("java:/comp/env/jdbc/BeholderDB");
            conn = dataSource.getConnection();
            PS_STORE_SHOT = conn.prepareStatement("INSERT INTO shots_tbl VALUES (NULL, ?, ?, ?, ?)");
            PS_UPDATE_PREVIEW = conn.prepareStatement("UPDATE preview_tbl SET time=?, image=?");
            logger.info("Database connection created");
        } catch (NamingException ne) {
            logger.error(ne, ne);
        } catch (SQLException se) {
            logger.error(se, se);
        }


    }

    //Resize original image to thumbnail
    protected byte[] getThumbnail(byte[] originalImage) {
        byte[] result = new byte[0];
        try {
            ByteArrayInputStream is = new ByteArrayInputStream(originalImage);
            BufferedImage buffOriginalImage = ImageIO.read(is);
            BufferedImage scaledImage = new BufferedImage(PREVIEW_WIDTH, PREVIEW_HEIGHT, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = scaledImage.createGraphics();
            g.drawImage(buffOriginalImage, 0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT, null);
            if("2".equals(System.getProperty("state"))) {
                g.setColor(Color.ORANGE);
                g.fillRect(0,0,8,6);
            }
            g.dispose();
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            ImageIO.write(scaledImage, "jpg", os);
            result = os.toByteArray();
            is.close();
            os.reset();
        } catch (Exception e) {
            logger.error(e, e);
        }
        return result;
    }

    protected void storeShot(byte[] data, String sessionId) throws IOException {
        logger.debug("Upload bytes available:" + data.length);
        InputStream dataIs = new ByteArrayInputStream(data);
        byte[] dataIs1 = getThumbnail(data);
        try {
            ps = PS_STORE_SHOT;
            PS_STORE_SHOT.setString(1, sessionId);
            PS_STORE_SHOT.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
            PS_STORE_SHOT.setBinaryStream(3, dataIs, dataIs.available());
            PS_STORE_SHOT.setBytes(4, dataIs1);
            int i = PS_STORE_SHOT.executeUpdate();
            System.out.println(i + " records added");
            dataIs.close();
        } catch (Exception e) {
            logger.error(e, e);
        } finally {
            clear();
        }
    }

    protected void storePreview(byte[] data) throws IOException {
        logger.debug("Upload bytes available:" + data.length);
        InputStream dataIs = new ByteArrayInputStream(data);
        try {
            ps = PS_UPDATE_PREVIEW;
            ps.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
            ps.setBinaryStream(2, dataIs, dataIs.available());
            int i = ps.executeUpdate();
            System.out.println(i + " records added");
            dataIs.close();
        } catch (Exception e) {
            logger.error(e, e);
        } finally {
            clear();
        }
    }

    @Override
    public void destroy() {
        super.destroy();
        try {
        	if (conn == null || conn.isClosed()) {
        		logger.debug("Connection is already closed");	
        	} else {
        		conn.close();	
        		logger.debug("Connection closed");        		
        	}
        } catch (SQLException e) {
            logger.error("Failed to close connection", e);
        }
    }

    protected Map getSettings() {
        Map result = new HashMap();
        result.put(STATE_PARAM, STATE_OFF);
        try {
            ps = getConnection().prepareStatement("SELECT state FROM settings_tbl");
            rs = ps.executeQuery();
            rs.next();
            String state = new String(rs.getString(STATE_PARAM));
            result.put(STATE_PARAM, state);
        } catch (SQLException e) {
            logger.error(e, e);
        }
        return result;
    }

    protected void deleteStoredImages() {
        try {
            ps = getConnection().prepareStatement("DELETE FROM shots_tbl");
            ps.execute();
        } catch (SQLException e) {
            logger.error(e, e);
        }
    }

    protected void updateSettings(Map newValue) {
        try {
            String state = ((String[])(newValue.get(STATE_PARAM)))[0];
            ps = getConnection().prepareStatement("UPDATE settings_tbl SET state=?");
            ps.setString(1, state);
            ps.execute();
            String propertyCode = "0";
            if (STATE_PRW.equals(state)) {
                propertyCode = "0";
            } else if (STATE_REC.equals(state)) {
                propertyCode = "1";
            } else if (STATE_HD.equals(state)) {
                propertyCode = "2";
            }
            System.setProperty("state", propertyCode);
        } catch (SQLException e) {
            logger.error(e, e);
        }
    }

    protected java.util.Date getPreviewTime() {
        java.util.Date result = new java.util.Date(0);
        try {
            ps = getConnection().prepareStatement("SELECT time from preview_tbl");
            ResultSet rs = ps.executeQuery();
            rs.next();
            result = new java.util.Date(rs.getTimestamp(1).getTime());
        } catch (SQLException e) {
            logger.error(e, e);
        } finally {
            return result;
        }
    }

    protected void closeResources () {
        try {
//            if (conn != null && conn.isClosed()) {
//                conn.close();
//            }
//            if (rs != null && !rs.isClosed()) {
//                rs.close();
//            }
            if (ps != null && !ps.isClosed()) {
                ps.close();
            }
        } catch (Exception e) {
            logger.error("Failed to close resources", e);
        }
    }

    protected void clear() {
        try {
            ps.clearBatch();
            ps.clearParameters();
        } catch (Exception e) {
            logger.error("Failed to close resources", e);
        }
    }

    protected InputStream getImageStream(String id) {
        InputStream is = null;
        try {
            ps = getConnection().prepareStatement("SELECT image FROM shots_tbl WHERE id=?");
            ps.setLong(1, Long.parseLong(id));
            rs = ps.executeQuery();
            rs.next();
            is = rs.getBinaryStream(1);
        } catch (SQLException e) {
            logger.error(e, e);
        }
        return is;
    }

    protected InputStream getImageStream() {
        InputStream is = null;
        try {
            ps = getConnection().prepareStatement("SELECT image FROM preview_tbl");
            rs = ps.executeQuery();
            rs.next();
            is = rs.getBinaryStream(1);
        } catch (SQLException e) {
            logger.error(e, e);
        }
        return is;
    }

    protected byte[] getPreviewBytes() {
        byte[] result = null;
        try {
            ps = getConnection().prepareStatement("SELECT image FROM preview_tbl");
            rs = ps.executeQuery();
            rs.next();
            result = rs.getBytes(1);
        } catch (SQLException e) {
            logger.error(e, e);
        } finally {
            try {
                rs.close();
            } catch (Exception e) {
                logger.error(e, e);
            }
        }
        return result;
    }

    protected InputStream getThumbStream(String id) {
        InputStream is = null;
        try {
            ps = getConnection().prepareStatement("SELECT thumbnail FROM shots_tbl WHERE id=?");
            ps.setLong(1, Long.parseLong(id));
            rs = ps.executeQuery();
            rs.next();
            is = rs.getBinaryStream(1);
        } catch (SQLException e) {
            logger.error(e, e);
        }
        return is;
    }

    protected List<Shot> getShots(int offset) {
        List<Shot> list = new ArrayList<Shot>();
        try {
            ps = getConnection().prepareStatement("SELECT id, time FROM shots_tbl ORDER BY time LIMIT ?, ?");
            ps.setInt(1, offset);
            ps.setInt(2, 100);
            rs = ps.executeQuery();
            while (rs.next()) {
                Shot shot = new Shot();
                shot.setId(rs.getLong(1));
                shot.setTime(rs.getTimestamp(2));
                list.add(shot);
            }
        } catch (SQLException e) {
            logger.error(e);
        }
        return list;
    }

    protected List<Shot> getShots() {
        List<Shot> list = new ArrayList<Shot>();
        try {
            ps = getConnection().prepareStatement("SELECT id, time FROM shots_tbl ORDER BY time");
            rs = ps.executeQuery();
            while (rs.next()) {
                Shot shot = new Shot();
                shot.setId(rs.getLong(1));
                shot.setTime(rs.getTimestamp(2));
                list.add(shot);
            }
        } catch (SQLException e) {
            logger.error(e);
        }
        return list;
    }

    private Connection getConnection() {
//        try {
//            if (conn != null && !conn.isClosed()) {
//                return conn;
//            }
//            InitialContext initialContext = new InitialContext();
//            DataSource dataSource = (DataSource)initialContext.lookup("java:/comp/env/jdbc/BeholderDB");
//            conn = dataSource.getConnection();
//            logger.info("Database connection created");
//        } catch (NamingException ne) {
//            logger.error(ne, ne);
//        } catch (SQLException se) {
//            logger.error(se, se);
//        }
        return conn;
    }


}
